/* Copyright (C) 2010 Fredrique Samuels, fredriquesamuels@gmail.com */

/*
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "component.h"

namespace tri {
namespace ui {

extDimensions::extDimensions(float _w, float _h) :
	w(_w), h(_h) {
}

void extDimensions::limitMax(extDimensions* d) {
	d->w = d->w < w ? d->w : w;
	d->h = d->h < h ? d->h : h;
}

void extDimensions::limitMin(extDimensions* d) {
	d->w = d->w < w ? w : d->w;
	d->h = d->h < h ? h : d->h;
}

tri::TTexture* getTexture(std::string _image) {
	tri::TTexture* tex= NULL;
	if (_image.compare("")!=0) {
		tex = tri::getTexture(_image);

		if (tex!=NULL) {
			if (!tex->created()) {
				tex->load(_image);
			}
		} else if (tri::sys::isFile(_image)) {
			tri::createTexture(_image);
			tex = tri::getTexture(_image);
			if (tex!=NULL) {
				tex->load(_image);
			}

		} else {
			tri::unbindAllTextures();
		}

	} else {
		tri::unbindAllTextures();
	}

	return tex;
}

//======================================================================

extComponent::extComponent() :
	tri::TEntity(), mStyle(""), mOverlayDistance(0.0), mLastState(-2),
			mFocusState(false), mCurrentStyleState(-2) {
	setSegmentInsets(0, 0, 0, 0);
	setImageInsets(0, 0, 0, 0);
	setInsets(0, 0, 0, 0);
	setClipInsets(0, 0, 0, 0);

	//setMaximumSize(10000,10000);

	mTopSegment.setUIDLoad(false);
	mTopLeftSegment.setUIDLoad(false);
	mTopRightSegment.setUIDLoad(false);
	mLeftSegment.setUIDLoad(false);
	mCenterSegment.setUIDLoad(false);
	mBottomSegment.setUIDLoad(false);
	mBottomLeftSegment.setUIDLoad(false);
	mBottomRightSegment.setUIDLoad(false);
	mLeftSegment.setUIDLoad(false);
	mRightSegment.setUIDLoad(false);

	setImage("");
	setStyle("DefaultComponentStyle");
}

extComponent::~extComponent() {
}

void extComponent::addEntity(TEntity* e) {
}

void extComponent::addComponent(extComponent* c) {
	TEntity::addEntity(c);
}

void extComponent::setImage(std::string im) {
	mTopSegment.setImage(im, false, false);
	mTopLeftSegment.setImage(im, false, false);
	mTopRightSegment.setImage(im, false, false);
	mLeftSegment.setImage(im, false, false);
	mCenterSegment.setImage(im, false, false);
	mBottomSegment.setImage(im, false, false);
	mBottomLeftSegment.setImage(im, false, false);
	mBottomRightSegment.setImage(im, false, false);
	mRightSegment.setImage(im, false, false);
}

void extComponent::setColor(int r, int g, int b, int a) {
	mTopSegment.setColor(r, g, b, a);
	mTopLeftSegment.setColor(r, g, b, a);
	mTopRightSegment.setColor(r, g, b, a);
	mLeftSegment.setColor(r, g, b, a);
	mCenterSegment.setColor(r, g, b, a);
	mBottomSegment.setColor(r, g, b, a);
	mBottomLeftSegment.setColor(r, g, b, a);
	mBottomRightSegment.setColor(r, g, b, a);
	mRightSegment.setColor(r, g, b, a);
}

void extComponent::setColor(tri::graphics::TColor c) {
	setColor(c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
}

void extComponent::update() {

	if (mLastState != getEventState() || mFocusState != hasFocus()) {
		mLastState = getEventState();
		mFocusState = hasFocus();
		this->updateStyle();
	}
	updateChildComponents();
	tri::TEntity::update();

}

void extComponent::setOverlayDistance(float distance) {
	mOverlayDistance = distance < 1.0 ? 1.0 : distance;
}

float extComponent::getOverlayDistance() {
	return mOverlayDistance;
}

void extComponent::display() {
	updateStyle();

	TVector v = getSize();

	if (isHidden()) {
		return;
	}

	float W = v.x;
	float H = v.y;
	float TH = mSegmentInsets.getTop();
	float CH = H - mSegmentInsets.getTop() - mSegmentInsets.getBottom();
	float BH = mSegmentInsets.getBottom();

	if (TH + BH > v.y) {
		TH = v.y/2.0;
		BH = v.y/2.0;
		CH = 0;
	}

	float LW = mSegmentInsets.getLeft();
	float CW = W - mSegmentInsets.getLeft() - mSegmentInsets.getRight();
	float RW = mSegmentInsets.getRight();

	if (LW + RW > v.x) {
		LW = v.x/2.0;
		RW = v.x/2.0;
		CW = 0;
	}

	float imW = 0;
	float imH = 0;
	if (mCenterSegment.getTexture() != NULL) {
		imW = mCenterSegment.getTexture()->getWidth();
		imH = mCenterSegment.getTexture()->getHeight();
	}
	float imT = mImageInsets.getTop();
	float imL = mImageInsets.getLeft();
	float imB = mImageInsets.getBottom();
	float imR = mImageInsets.getRight();

	mTopSegment.setSize(CW, TH, 0);
	mTopSegment.setPosition(LW/2-RW/2+W/2, H/2-TH/2+H/2, 0);
	mTopSegment.setImageInsets(imL, 0, imR, imH-imT);
	mTopSegment.setClipInsets(mClipInsets.getLeft()<=LW ? 0
			: mClipInsets.getLeft()-LW, mClipInsets.getTop(),
			mClipInsets.getRight()<=RW ? 0 : mClipInsets.getRight()-RW,
			mClipInsets.getBottom()<=BH+CH ? 0 : mClipInsets.getBottom()-BH-CH);
	mTopSegment.render();

	mTopLeftSegment.setSize(LW, TH, 0);
	mTopLeftSegment.setPosition(-W/2+LW/2+W/2, H/2-TH/2+H/2, 0);
	mTopLeftSegment.setImageInsets(0, 0, imW-imL, imH-imT);
	mTopLeftSegment.setClipInsets(mClipInsets.getLeft(), mClipInsets.getTop(),
			mClipInsets.getRight()<=RW+CW ? 0 : mClipInsets.getRight()-RW-CW,
			mClipInsets.getBottom()<=BH+CH ? 0 : mClipInsets.getBottom()-BH-CH);
	mTopLeftSegment.render();

	mTopRightSegment.setSize(RW, TH, 0);
	mTopRightSegment.setPosition(W/2-RW/2+W/2, H/2-TH/2+H/2, 0);
	mTopRightSegment.setImageInsets(imW-imR, 0, 0, imH-imT);
	mTopRightSegment.setClipInsets(mClipInsets.getLeft()<=LW+CW ? 0
			: mClipInsets.getLeft()-LW-CW, mClipInsets.getTop(),
			mClipInsets.getRight(), mClipInsets.getBottom()<=BH+CH ? 0
					: mClipInsets.getBottom()-BH-CH);
	mTopRightSegment.render();

	mLeftSegment.setSize(LW, CH, 0);
	mLeftSegment.setPosition(-W/2+LW/2+W/2, BH/2-TH/2+H/2, 0);
	mLeftSegment.setImageInsets(0, imT, imW-imL, imB);
	mLeftSegment.setClipInsets(mClipInsets.getLeft(),
			mClipInsets.getTop()<=TH ? 0 : mClipInsets.getTop()- TH,
			mClipInsets.getRight()<=RW+CW ? 0 : mClipInsets.getRight()-RW-CW,
			mClipInsets.getBottom()<=BH ? 0 : mClipInsets.getBottom()-BH);
	mLeftSegment.render();

	mCenterSegment.setSize(CW, CH, 0);
	mCenterSegment.setPosition(LW/2-RW/2+W/2, BH/2-TH/2+H/2, 0);
	mCenterSegment.setImageInsets(imL, imT, imR, imB);
	mCenterSegment.setClipInsets(mClipInsets.getLeft()<=LW ? 0
			: mClipInsets.getLeft()-LW, mClipInsets.getTop()<=TH ? 0
			: mClipInsets.getTop()- TH, mClipInsets.getRight()<=RW ? 0
			: mClipInsets.getRight()-RW, mClipInsets.getBottom()<=BH ? 0
			: mClipInsets.getBottom()-BH);
	mCenterSegment.render();

	mRightSegment.setSize(RW, CH, 0);
	mRightSegment.setPosition(W/2-RW/2+W/2, BH/2-TH/2+H/2, 0);
	mRightSegment.setImageInsets(imW-imR, imT, 0, imB);
	mRightSegment.setClipInsets(mClipInsets.getLeft()<=LW+CW ? 0
			: mClipInsets.getLeft()-LW-CW, mClipInsets.getTop()<=TH ? 0
			: mClipInsets.getTop()- TH, mClipInsets.getRight(),
			mClipInsets.getBottom()<=BH ? 0 : mClipInsets.getBottom()-BH);
	mRightSegment.render();

	mBottomSegment.setSize(CW, BH, 0);
	mBottomSegment.setPosition(LW/2-RW/2+W/2, -H/2+BH/2+H/2, 0);
	mBottomSegment.setImageInsets(imL, imH-imB, imR, 0);
	mBottomSegment.setClipInsets(mClipInsets.getLeft()<=LW ? 0
			: mClipInsets.getLeft()-LW, mClipInsets.getTop()<=TH+CH ? 0
			: mClipInsets.getTop()-TH-CH, mClipInsets.getRight()<=RW ? 0
			: mClipInsets.getRight()-RW, mClipInsets.getBottom());
	mBottomSegment.render();

	mBottomLeftSegment.setSize(LW, BH, 0);
	mBottomLeftSegment.setPosition(-W/2+LW/2+W/2, -H/2+BH/2+H/2, 0);
	mBottomLeftSegment.setImageInsets(0, imH-imB, imW-imL, 0);
	mBottomLeftSegment.setClipInsets(mClipInsets.getLeft(),
			mClipInsets.getTop()<=TH+CH ? 0 : mClipInsets.getTop()-TH-CH,
			mClipInsets.getRight()<=RW+CW ? 0 : mClipInsets.getRight()-RW-CW,
			mClipInsets.getBottom());
	mBottomLeftSegment.render();

	mBottomRightSegment.setSize(RW, BH, 0);
	mBottomRightSegment.setPosition(W/2-RW/2+W/2, -H/2+BH/2+H/2, 0);
	mBottomRightSegment.setImageInsets(imW-imR, imH-imB, 0, 0);
	mBottomRightSegment.setClipInsets(mClipInsets.getLeft()<=LW+CW ? 0
			: mClipInsets.getLeft()-LW-CW, mClipInsets.getTop()<=TH+CH ? 0
			: mClipInsets.getTop()-TH-CH, mClipInsets.getRight(),
			mClipInsets.getBottom());
	mBottomRightSegment.render();
}

void extComponent::setSegmentInsets(float l, float t, float r, float b) {
	mSegmentInsets=tri::TRectangle(l, t, r, b);
	updateStyle();
}

void extComponent::setImageInsets(float l, float t, float r, float b) {
	mImageInsets=tri::TRectangle(l, t, r, b);
	updateStyle();
}

void extComponent::setClipInsets(float l, float t, float r, float b) {
	mClipInsets=tri::TRectangle(l, t, r, b);
	updateStyle();
}

void extComponent::setInsets(float l, float t, float r, float b) {
	mInsets=tri::TRectangle(l, t, r, b);
}

void extComponent::applyStyle(int state) {
	extStyle* style = extStyle::getStyle(mStyle);
	if (style!=NULL) {
		tri::graphics::TColor c = style->getColor(state);
		setColor(c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
		setImage(style->getBGImage(state));
		setSegmentInsets(style->getSegmentInsets(state));
		setImageInsets(style->getImageInsets(state));
		setInsets(style->getInsets(state));
	}
}

bool extComponent::updateStyle() {
	if (extStyle::exists(mStyle) && mStyle!="") {
		int state = hasFocus() && (mLastState == tri::NONE || mLastState
				== tri::MOUSE_OVER ) ? tri::FOCUS : mLastState;
		state = isEnabled() ? state : tri::DISABLED;
		if (state == tri::FOCUS || state == tri::DISABLED) {
			mLastState = tri::NONE;
		}

		bool apply_new = true;//mCurrentStyleState != state;
		if (apply_new ) {
			mCurrentStyleState = state;
			applyStyle(state);
		}

		return true;
	}
	return false;
}

void extComponent::setInsets(tri::TRectangle* r) {
	if (r!=NULL) {
		mInsets.setValue(r);
	}
}

void extComponent::pack() {
	std::set<TEntity*> EntSet;
	getEntitySet(EntSet);

	std::set<TEntity*>::const_iterator it = EntSet.begin();
	std::set<TEntity*>::const_iterator itEnd = EntSet.end();

	while (it != itEnd) {
		((extComponent*)(*it))->pack();
		++it;
	}
}

void extComponent::updateChildComponents() {
	std::set<TEntity*> EntSet;
	getEntitySet(EntSet);

	std::set<TEntity*>::const_iterator it = EntSet.begin();
	std::set<TEntity*>::const_iterator itEnd = EntSet.end();

	while (it != itEnd) {
		extComponent* comp = ((extComponent*)(*it));
		TVector pos = comp->getPosition();
		pos.z = comp->getOverlayDistance();
		comp->setPosition(pos);
		comp->setRotation(0, 0, 0);

		TVector ps;
		ps = getSize();
		TVector cp;
		cp = comp->getPosition();
		TVector cs;
		cs = comp->getSize();

		float clip[4]= { 0, 0, 0, 0 };
		clip[0] = (mInsets.getLeft() + mClipInsets.getLeft() ) - cp.x;
		clip[1] = (cp.y + cs.y) -(ps.y - mInsets.getTop()
				- mClipInsets.getTop() );
		clip[2] = (cp.x + cs.x) -(ps.x - mInsets.getRight()
				- mClipInsets.getRight() );
		clip[3] = (mInsets.getBottom() + mClipInsets.getBottom() ) - cp.y;

		for (int i = 0; i<4; i++) {
			clip[i] = clip[i]<0 ? 0 : clip[i];
		}

		comp->setClipInsets(clip[0], clip[1], clip[2], clip[3]);
//		comp->setClipInsets(0,0,0,0);
		++it;
	}
}

bool extComponent::childHasFocus() {
	std::set<TEntity*> EntSet;
	getEntitySet(EntSet);

	std::set<TEntity*>::const_iterator it = EntSet.begin();
	std::set<TEntity*>::const_iterator itEnd = EntSet.end();

	while (it != itEnd) {
		if (((extComponent*)(*it))->hasFocus() || ((extComponent*)(*it))->childHasFocus()) {
			return true;
		}
		++it;
	}

	return false;
}

void extComponent::getComponents(std::vector<extComponent*> &list){
	list.clear();
	
	std::set<TEntity*> EntSet;
	getEntitySet(EntSet);

	std::set<TEntity*>::const_iterator it = EntSet.begin();
	std::set<TEntity*>::const_iterator itEnd = EntSet.end();

	while (it != itEnd) {
		list.push_back((extComponent*)(*it));
		++it;
	}
}

std::string extComponent::getStyle() {
	return mStyle;
}

void extComponent::setStyle(std::string style) {
	mStyle = style;
}

void extComponent::setSegmentInsets(tri::TRectangle* r) {
	if (r!=NULL) {
		mSegmentInsets.setValue(r);
	}
}

void extComponent::setImageInsets(tri::TRectangle* r) {
	if (r!=NULL) {
		mImageInsets.setValue(r);
	}
}

tri::TRectangle extComponent::getInsets() {
	return mInsets;
}

tri::TRectangle extComponent::getClipInsets() {
	return mClipInsets;
}

bool extComponent::isHidden() {
	TVector v;
	v = getSize();
	return !isVisible() || (mClipInsets.getLeft() + mClipInsets.getRight()
			>= v.x|| mClipInsets.getTop() + mClipInsets.getBottom() >= v.y);
}

}
}
